/*
 *    This file is part of CeluMap.
 *
 *    CeluMap is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    CeluMap is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Copyright 2006, 2007, 2008 Gonzalo Merayo
 *    gonzalo (at) celumap.com.ar
 *    www.celumap.com.ar
 *
 */

package geometry;


public class MovingTransform {

	Matrix center1 = new Matrix(); //Center, screen scaling
	Matrix scale = new Matrix(); //Scale, map 2 screen scaling.
	Matrix center2 = new Matrix(); //Center, map scaling
	Matrix translate = new Matrix(); //Translado, map scaling
	
	long xDespl = 0;
	long yDespl = 0;//En coordenadas de map, no de screen
	long xCentre = 0;
	long yCentre = 0;//En coordenadas de Screen, no de map.
	long zoom = Matrix.pfshift;
	//Calculadas
	Matrix transform = new Matrix();//Esta es map2screen
	Matrix inverse = new Matrix();//Esta es screen2map
	
	boolean rotate = false;
	
	public MovingTransform(){
		translate.setIdentity();
		transform.setIdentity();
		inverse.setIdentity();
		scale.setIdentity();
		center1.setIdentity();
		center2.setIdentity();
		generateTransform();
	}
	private void generateTransform(){
		//First Center for zoom,
		//then Scale,
		//then unCenter, (this time in screen scaling)
		//then Translation,
		transform=center1.mul(scale).mul(center2).mul(translate);
		transform.getInverse(inverse);
	}
	
	public void screen2map(Point o, Point d) {
		d.x = screenX2map(o.x, o.y);
		d.y = screenY2map(o.x, o.y);
	}
	public void map2screen(Point o, Point d) {
		d.x = mapX2screen(o.x, o.y);
		d.y = mapY2screen(o.x, o.y);
	}
	public void screen2map(Rectangle o, Rectangle d) {
		d.bbxi = screenX2map(o.bbxi,o.bbyi);
		d.bbyi = screenY2map(o.bbxi,o.bbyi);
		d.bbxf = screenX2map(o.bbxf,o.bbyf);
		d.bbyf = screenY2map(o.bbxf,o.bbyf);
		d.sort();
	}
	public void map2screen(Rectangle o, Rectangle d) {
		d.bbxi = mapX2screen(o.bbxi,o.bbyi);
		d.bbyi = mapY2screen(o.bbxi,o.bbyi);
		d.bbxf = mapX2screen(o.bbxf,o.bbyf);
		d.bbyf = mapY2screen(o.bbxf,o.bbyf);
		d.sort();
	}
	public void translate(long x, long y) {//coordenadas Screen
		xDespl+=(x<<(Matrix.nshift*2))/zoom;
		yDespl+=(y<<(Matrix.nshift*2))/zoom;
		translate.setTranslate(xDespl, yDespl);
		generateTransform();
	}
	public void setScreenCentre(int x, int y){
		xCentre=x<<Matrix.nshift;
		yCentre=y<<Matrix.nshift;		
		adjustCenterMatrix();
		generateTransform();
	}
	final private void adjustCenterMatrix(){
		center1.setTranslate(-xCentre, -yCentre);
		center2.setTranslate((xCentre<<Matrix.nshift)/zoom, (yCentre<<Matrix.nshift)/zoom);
	}
	public final int screenX2map(int x, int y) {
		if(rotate)
			return -inverse.muly(x, y);
		return inverse.mulx(x, y);
		
	}
	public final int screenY2map(int x, int y) {
		if(rotate)
			return inverse.mulx(x, y);
		return inverse.muly(x, y);
	}
	public final int mapY2screen(int x, int y) {
		if(rotate)
			return transform.muly(y, -x);
		return transform.muly(x, y);
	}
	public final int mapX2screen(int x, int y) {
		if(rotate)
			return transform.mulx(y, -x);
		return transform.mulx(x, y);
	}
	public void zoom(long d) {
		setZoom((zoom*d)>>Matrix.nshift);
	}
	public long getZoom() {
		return zoom;
	}
	public void setZoom(long d) {
		zoom =d;
		scale.setScale(zoom);
		adjustCenterMatrix();
		generateTransform();
	}
	public void adjust(Rectangle a, int screenWidth, int screenHeight) {
		if(rotate){
			setZoom(Math.min(
					(screenWidth<<Matrix.nshift)/Math.abs(a.bbyf-a.bbyi),
					(screenHeight<<Matrix.nshift)/Math.abs(a.bbxf-a.bbxi)));
			xDespl=-(a.bbxi+a.bbxf+screenHeight)*Matrix.pfshift/2;
			yDespl=((a.bbyi+a.bbyf)/2-((screenHeight*zoom)>>(Matrix.nshift+1)))<<Matrix.nshift;
		}else{
			setZoom(Math.min(
					(screenHeight<<Matrix.nshift)/Math.abs(a.bbyf-a.bbyi),
					(screenWidth<<Matrix.nshift)/Math.abs(a.bbxf-a.bbxi)));
			translate.setTranslate(0, 0);
			generateTransform();
			xDespl = (((((screenWidth/2 - mapX2screen((a.bbxf+a.bbxi)/2, (a.bbyf+a.bbyi)/2)))<<Matrix.nshift)/zoom)<<Matrix.nshift);
			yDespl = (((((screenHeight/2 - mapY2screen((a.bbxf+a.bbxi)/2, (a.bbyf+a.bbyi)/2)))<<Matrix.nshift)/zoom)<<Matrix.nshift);
		}
		translate.setTranslate(xDespl, yDespl);
		generateTransform();
	}
	/*
	public String toString(){
		StringBuffer sb = new StringBuffer();
		sb.append("center1:"+center1.toString()+"\n");
		sb.append("scale:"+scale.toString()+"\n");
		sb.append("center2:"+center2.toString()+"\n");
		sb.append("translate:"+translate.toString()+"\n");
		sb.append("transform:"+transform.toString()+"\n");
		sb.append("inverse:"+inverse.toString()+"\n");
		sb.append("zoom:"+zoom+"\n");
		//long xDespl = 0;
		//long yDespl = 0;//En coordenadas de map, no de screen
		//long xCentre = 0;
		//long yCentre = 0;//En coordenadas de Screen, no de map.
	
		return sb.toString();
	}
	*/
	public long getXCentre() {
		return xCentre>>Matrix.nshift;
	}
	public long getYCentre() {
		return yCentre>>Matrix.nshift;
	}
}
